﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using MyTRCP.Common.Models;
using System.ComponentModel;
using MyTRCP.Common;
using Microsoft.Extensions.Hosting;
using MyTRCP.Common.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using InTheHand.Net;
using System.Runtime.InteropServices;

namespace MyTRCP.Client
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private readonly Dictionary<byte, ChannelInfo> _channelInRegionDic;
        private readonly Dictionary<byte, string> _regionNameDic;
        private readonly Dictionary<byte, Button> _channelButtonDic;
        private DevSettings? _devSettings;
        private DevInfoType3? _devInfo;
        private VolumeInfo? _volumeInfo;
        private DevStatus? _devStatus;
        private readonly MyCurrentChannel _currChA;
        private readonly MyCurrentChannel _currChB;
        private readonly System.Timers.Timer _timer;
        private readonly MyBatteryInfo _battInfo;
        private bool _pttStatus;
        private readonly ILogger<MainWindow> _logger;
        private readonly ClientConfigService _ccs;
        private readonly DevCtrlLinkService _dcls;
        private readonly DevAudioLinkService _dals;
        private readonly MyProtocolClientSideService _mpcss;
        private readonly DevCmdService _dcs;
        private readonly AudioAvgLouldnessComputeService _aalcs;
        private readonly AudioRecordService _ars;
        private readonly AudioPlayService _aps;
        private readonly MicRecordService _mrs;
        private bool _chSwitching;
        public MainWindow(ILogger<MainWindow> logger, ClientConfigService ccs, DevCtrlLinkService dcls, DevAudioLinkService dals, MyProtocolClientSideService mpcss, DevCmdService dcs, AudioAvgLouldnessComputeService aalcs, AudioRecordService ars, AudioPlayService aps, MicRecordService mrs)
        {
            _logger = logger;
            _ccs = ccs;
            _dcls = dcls;
            _dals = dals;
            _dcs = dcs;
            _mpcss = mpcss;
            _aalcs = aalcs;
            _ars = ars;
            _aps = aps;
            _mrs = mrs;
            InitializeComponent();
            _dcs.NotificationReceived += DevCmdManager_NotificationReceived;
            _dcs.DevSettingsChanged += DevCmdManager_DevSettingsChanged;
            _dcs.DevStatusChanged += DevCmdManager_DevStatusChanged;
            _dcs.NewCmdLatencyComputed += DevCmdManager_NewCmdLatencyComputed;
            _channelInRegionDic = new Dictionary<byte, ChannelInfo>();
            _regionNameDic = new Dictionary<byte, string>();
            _channelButtonDic = new Dictionary<byte, Button>();
            if (_ccs.ClientMode == ClientModeEnum.Local)
            {
                _dcls.Connected += DeviceControlManager_Connected;
                _dcls.Disconnected += DeviceControlManager_Disconnected;
            }
            else if (_ccs.ClientMode == ClientModeEnum.Remote)
            {
                _mpcss.DevCtrlConnected += DeviceControlManager_Connected;
                _mpcss.DevCtrlDisconnected += DeviceControlManager_Disconnected;
            }
            _aalcs.LouldnessChanged += AudioPlayManager_LouldnessChanged;

            _battInfo = new MyBatteryInfo();

            _timer = new System.Timers.Timer(10000);
            _timer.Elapsed += _timer_Elapsed;


            _currChA = new MyCurrentChannel() { IsEmpty = true, VisCh = Visibility.Collapsed };
            _currChB = new MyCurrentChannel() { IsEmpty = true, VisCh = Visibility.Collapsed };

            ic_curr_ch.Items.Add(_currChA);
            ic_curr_ch.Items.Add(_currChB);

            l_batt_percent.DataContext = _battInfo;
            l_batt_volt.DataContext = _battInfo;

            var waveOutDic = _aps.GetWaveOutDevCapDic();
            var waveInDic = _mrs.GetWaveInDevCapDic();
            if (waveOutDic.Count > 0)
            {
                ComboBoxItem cbi = new ComboBoxItem();
                cbi.Content = "默认设备";
                cbi.DataContext = -1;
                cb_waveoutdev.Items.Add(cbi);
            }
            foreach (var dev in waveOutDic)
            {
                ComboBoxItem cbi = new ComboBoxItem();
                cbi.Content = dev.Value.ProductName;
                cbi.DataContext = dev.Key;
                cb_waveoutdev.Items.Add(cbi);
            }
            foreach (var dev in waveInDic)
            {
                ComboBoxItem cbi = new ComboBoxItem();
                cbi.Content = dev.Value.ProductName;
                cbi.DataContext = dev.Key;
                cb_waveindev.Items.Add(cbi);
            }
            if (cb_waveoutdev.Items.Count > 0)
                cb_waveoutdev.SelectedIndex = 0;
            if (cb_waveindev.Items.Count > 0)
            {
                cb_waveindev.SelectedIndex = 0;
                b_ptt.IsEnabled = true;
            }
            else
            {
                b_ptt.IsEnabled = false;
            }
            cb_waveoutdev.SelectionChanged += Cb_waveoutdev_SelectionChanged;
            cb_waveindev.SelectionChanged += Cb_waveindev_SelectionChanged;
            b_ptt.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(b_ptt_MouseLeftButtonDown), true);
            b_ptt.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(b_ptt_MouseLeftButtonUp), true);

            FillDevMacsComboBox();
        }
        private void FillDevMacsComboBox()
        {
            cb_mac.ItemStringFormat = "C";
            cb_mac.Items.Clear();
            foreach (BluetoothAddress mac in _ccs.SavedDevMacs)
            {
                cb_mac.Items.Add(mac);
            }
            if (cb_mac.Items.Count > _ccs.LastConnDevIndex)
            {
                cb_mac.SelectedIndex = _ccs.LastConnDevIndex;
            }
            else if (cb_mac.Items.Count > 0)
            {
                cb_mac.SelectedIndex = 0;
            }
        }

        private void DevCmdManager_NewCmdLatencyComputed(object? sender, TimeSpan e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                l_cmd_latency.Content = $"{e.TotalMilliseconds:0}ms";
            });
        }

        private void Cb_waveindev_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cb_waveindev.SelectedItem is ComboBoxItem cbi)
            {
                if (cbi.DataContext is int devId)
                {
                    if (!_mrs.ChangeRecordDevice(devId))
                    {
                        MessageBox.Show("切换录音设备失败");
                    }
                }
            }
        }

        private void Cb_waveoutdev_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cb_waveoutdev.SelectedItem is ComboBoxItem cbi)
            {
                if (cbi.DataContext is int devId)
                {
                    if (!_aps.ChangePlayDevice(devId))
                    {
                        MessageBox.Show("切换播放设备失败");
                    }
                }
            }
        }

        private void AudioPlayManager_LouldnessChanged(object? sender, short e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                pb_ln.Value = (double)(e / 32767m * 100);
            });
        }

        private async void _timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            if (_dcls.IsConnected)
            {
                try
                {
                    _battInfo.BatteryVoltage = await _dcs.ReadStatusBatteryVoltage();
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"exception occurred during getting batt info.Msg:{ex.Message}");
                }
            }
        }

        private void DeviceControlManager_Disconnected(object? sender, EventArgs e)
        {
            _logger.LogInformation("控制链已断开");
            _timer.Stop();
        }

        private void DeviceControlManager_Connected(object? sender, EventArgs e)
        {
            _logger.LogInformation("控制链已连接");
            InitDevInfo();
            _timer.Start();
        }

        private void DevCmdManager_DevStatusChanged(object? sender, DevStatus e)
        {
            _devStatus = e;
            UpdateMyCurrentChannelsWhenStatusChanged();
            _logger.LogDebug("设备状态变更：{0}", e.ToString());
            if (e.InRx || e.InTx)
            {
                ChannelInfo ci = _channelInRegionDic[e.CurrentChannel];
                _ars.NowRecordingMemo = $"{ci.Name}@{(e.InRx ? ci.RxFreq : ci.TxFreq) / 1000000m}";
            }
        }

        private void DevCmdManager_DevSettingsChanged(object? sender, DevSettings e)
        {
            _devSettings = e;
            UpdateMyCurrentChannelsWhenSettingChanged();
            _logger.LogDebug("设备设置变更：{0}", e.ToString());
        }
        private void UpdateMyCurrentChannelsWhenSettingChanged()
        {
            Dispatcher.BeginInvoke(() =>
            {
                if (_devSettings != null)
                {
                    // 更新单双守模式
                    if (_devSettings.Scan)
                    {
                        rb_scan.IsChecked = true;
                        rb_single.IsChecked = false;
                        rb_dual.IsChecked = false;
                    }
                    else if (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off)
                    {
                        rb_dual.IsChecked = true;
                        rb_single.IsChecked = false;
                        rb_scan.IsChecked = false;
                    }
                    else
                    {
                        rb_single.IsChecked = true;
                        rb_dual.IsChecked = false;
                        rb_scan.IsChecked = false;
                    }

                    // 更新当前信道
                    _currChB.VisCh = (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off || _devSettings.Scan) ? Visibility.Visible : Visibility.Collapsed;
                    if (_channelInRegionDic.TryGetValue(_devSettings.ChannelA, out ChannelInfo? cha))
                    {
                        _currChA.Id = _devSettings.ChannelA;
                        _currChA.ChannelName = cha.Name;
                        _currChA.TxFreq = cha.TxFreq;
                        _currChA.RxFreq = cha.RxFreq;
                        _currChA.IsEmpty = cha.TxFreq == 0 && cha.RxFreq == 0;
                    }
                    else
                    {
                        _currChA.IsEmpty = true;
                    }
                    if (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off || (_devSettings.Scan && _currChB.IsEmpty))
                    {
                        if (_channelInRegionDic.TryGetValue(_devSettings.ChannelB, out ChannelInfo? chb))
                        {
                            _currChB.Id = _devSettings.ChannelB;
                            _currChB.ChannelName = chb.Name;
                            _currChB.TxFreq = chb.TxFreq;
                            _currChB.RxFreq = chb.RxFreq;
                            _currChB.IsEmpty = chb.TxFreq == 0 && chb.RxFreq == 0;
                        }
                        else
                        {
                            _currChB.IsEmpty = true;
                        }
                    }
                    if (_devSettings.Scan)
                    {
                        if (ic_curr_ch.SelectedItem == null)
                        {
                            ic_curr_ch.SelectedItem = _currChA;
                        }
                    }
                    else
                    {
                        if (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off)
                        {
                            if (_devSettings.DoubleChannel == DoubleChannelTypeEnum.A)
                            {
                                ic_curr_ch.SelectedItem = _currChA;
                            }
                            else
                            {
                                ic_curr_ch.SelectedItem = _currChB;
                            }
                        }
                        else
                        {
                            ic_curr_ch.SelectedItem = _currChA;
                        }
                    }
                    if (ic_curr_ch.SelectedItem is MyCurrentChannel cc)
                    {
                        if (_channelInRegionDic.TryGetValue(cc.Id, out ChannelInfo? ci))
                        {
                            switch (ci.TxPower)
                            {
                                case 0:
                                    rb_curr_ch_power_high.IsChecked = true;
                                    break;
                                case -1:
                                    rb_curr_ch_power_low.IsChecked = true;
                                    break;
                                case -2:
                                    rb_curr_ch_power_middle.IsChecked = true;
                                    break;
                            }
                        }
                    }
                }
            });
        }
        private void UpdateMyCurrentChannelsWhenStatusChanged()
        {
            Dispatcher.BeginInvoke(() =>
            {
                if (_devStatus != null)
                {
                    if (!_devStatus.PowerOn)
                    {
                        _currChA.VisCh = Visibility.Collapsed;
                        _currChB.VisCh = Visibility.Collapsed;
                    }
                    else
                    {
                        _currChA.VisCh = Visibility.Visible;
                        if (_devStatus.Scan)
                        {
                            if (_currChA.Id == _devStatus.CurrentChannel)
                            {

                            }
                            else
                            {
                                if (_channelInRegionDic.TryGetValue(_devStatus.CurrentChannel, out ChannelInfo? chb))
                                {
                                    _currChB.Id = _devStatus.CurrentChannel;
                                    _currChB.ChannelName = chb.Name;
                                    _currChB.TxFreq = chb.TxFreq;
                                    _currChB.RxFreq = chb.RxFreq;
                                    _currChB.IsEmpty = chb.TxFreq == 0 && chb.RxFreq == 0;
                                }
                                else
                                {
                                    _currChB.IsEmpty = true;
                                }
                            }
                            _currChB.VisCh = Visibility.Visible;
                        }
                        else
                        {
                            if (_devStatus.DoubleChannel == DoubleChannelTypeEnum.Off)
                            {
                                _currChB.VisCh = Visibility.Collapsed;
                            }
                            else
                            {
                                _currChB.VisCh = Visibility.Visible;
                            }
                        }
                    }
                    if (_devStatus.InRx)
                    {
                        if (_devStatus.CurrentChannel == _currChA.Id)
                        {
                            _currChA.VisInRx = Visibility.Visible;
                        }
                        else
                        {
                            _currChA.VisInRx = Visibility.Hidden;
                        }
                        if (_devStatus.CurrentChannel == _currChB.Id)
                        {
                            _currChB.VisInRx = Visibility.Visible;
                        }
                        else
                        {
                            _currChB.VisInRx = Visibility.Hidden;
                        }
                    }
                    else
                    {
                        _currChA.VisInRx = Visibility.Hidden;
                        _currChB.VisInRx = Visibility.Hidden;
                    }
                    if (_devStatus.InTx)
                    {
                        if (_devStatus.CurrentChannel == _currChA.Id)
                        {
                            _currChA.VisInTx = Visibility.Visible;
                        }
                        else
                        {
                            _currChA.VisInRx = Visibility.Hidden;
                        }
                        if (_devStatus.CurrentChannel == _currChB.Id)
                        {
                            _currChB.VisInTx = Visibility.Visible;
                        }
                        else
                        {
                            _currChB.VisInRx = Visibility.Hidden;
                        }
                    }
                    else
                    {
                        _currChA.VisInTx = Visibility.Hidden;
                        _currChB.VisInTx = Visibility.Hidden;
                    }
                    pb_rssi.Value = _devStatus.RSSI;
                }
            });
        }
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            b_conn.IsEnabled = false;
            try
            {
                if (cb_mac.SelectedItem != null && cb_mac.SelectedItem is BluetoothAddress ba)
                {
                    if (_ccs.ClientMode == ClientModeEnum.Local)
                    {
                        if (!_dcls.IsConnected)
                        {
                            if (await _dcls.OpenAsync(ba))
                            {
                                if (!_dals.IsConnected)
                                {
                                    if (!await _dals.OpenAsync(ba))
                                    {
                                        MessageBox.Show("语音链连接失败");
                                    }
                                }
                                b_conn.Content = "断开连接";
                                SystemSleepManagement.PreventSleep();
                            }
                            else
                            {
                                MessageBox.Show("控制链连接失败");
                            }
                        }
                        else
                        {
                            if (_dals.IsConnected)
                            {
                                if (!await _dals.CloseAsync())
                                {
                                    MessageBox.Show("语音链断开失败");
                                }
                            }
                            if (!await _dcls.CloseAsync())
                            {
                                MessageBox.Show("控制链断开失败");
                            }
                            if (!_dcls.IsConnected && !_dals.IsConnected)
                            {
                                b_conn.Content = "连接选择的设备";
                                SystemSleepManagement.ResotreSleep();
                            }
                        }
                    }
                    else if (_ccs.ClientMode == ClientModeEnum.Remote)
                    {
                        if (_mpcss.IsCtrlConnected)
                        {
                            if (!await _mpcss.DisconnectRemoteCtrlChannel())
                            {
                                MessageBox.Show("控制链断开失败");
                            }
                            if (_mpcss.IsAudioConnected)
                            {
                                if (!await _mpcss.DisconnectRemoteAudioChannel())
                                {
                                    MessageBox.Show("语音链断开失败");
                                }
                            }
                            if (!_mpcss.IsCtrlConnected && !_mpcss.IsAudioConnected)
                            {
                                b_conn.Content = "连接选择的设备";
                                SystemSleepManagement.ResotreSleep();
                            }
                        }
                        else
                        {
                            if (await _mpcss.ConnectRemoteCtrlChannel(ba))
                            {
                                if (!_mpcss.IsAudioConnected)
                                {
                                    if (!await _mpcss.ConnectRemoteAudioChannel(ba))
                                    {
                                        MessageBox.Show("语音链连接失败");
                                    }
                                }
                                DeviceControlManager_Connected(this, EventArgs.Empty);
                                b_conn.Content = "断开连接";
                                SystemSleepManagement.PreventSleep();
                            }
                            else
                            {
                                MessageBox.Show("控制链连接失败");
                            }
                        }
                    }
                    _ccs.LastConnDevIndex = cb_mac.SelectedIndex;
                    _ccs.SaveLastConnDevIndex();
                }
                else
                {
                    MessageBox.Show("未选择mac地址，如空请先添加");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            b_conn.IsEnabled = true;
        }


        private void DevCmdManager_NotificationReceived(object? sender, NotificationReceivedEventArgs e)
        {
            _logger.LogDebug($"type={e.Type},data={Tools.ByteArray2HexString(e.Data)}");
        }

        private async void InitDevInfo()
        {
            try
            {
                _devInfo = await _dcs.GetDevInfoType3();
                _devSettings = await _dcs.ReadSettings();
                _volumeInfo = await _dcs.GetVolume();
                _devStatus = await _dcs.GetHtStatus();
                await _dcs.RegisterNotification(NotificationEventTypeEnum.HT_STATUS_CHANGED);
                _battInfo.BatteryVoltage = await _dcs.ReadStatusBatteryVoltage();
                for (byte i = 0; i < _devInfo.ChannelCount; i++)
                {
                    var ch = await _dcs.ReadRfCh(i);
                    await Dispatcher.BeginInvoke(() =>
                    {
                        if (!_channelInRegionDic.ContainsKey(ch.Id))
                        {
                            _channelInRegionDic.Add(ch.Id, ch.ChannelInfo);
                        }
                        else
                        {
                            _channelInRegionDic[ch.Id] = ch.ChannelInfo;
                        }
                        if (!_channelButtonDic.ContainsKey(ch.Id))
                        {
                            int row = ch.Id / 4;
                            int col = ch.Id % 4;
                            while (wp_ch.RowDefinitions.Count < row + 1)
                            {
                                wp_ch.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(50, GridUnitType.Pixel) });
                            }
                            MyChannelButtonBindingData data = new MyChannelButtonBindingData(ch.Id, ch.ChannelInfo);
                            Button b = new Button();
                            b.DataContext = data;
                            b.SetBinding(ContentProperty, nameof(data.ButtonDisplayContent));
                            b.SetValue(Grid.ColumnProperty, col);
                            b.SetValue(Grid.RowProperty, row);
                            b.SetValue(Block.TextAlignmentProperty, TextAlignment.Center);
                            b.Click += Button_Channel_Click;
                            wp_ch.Children.Add(b);
                            _channelButtonDic.Add(ch.Id, b);
                        }
                        if (_channelButtonDic[ch.Id].DataContext is MyChannelButtonBindingData dd)
                        {
                            dd.ReplaceChannelInfo(ch.ChannelInfo);
                        }
                        else
                        {
                            _channelButtonDic[ch.Id].DataContext = null;
                            _channelButtonDic[ch.Id].DataContext = new MyChannelButtonBindingData(ch.Id, ch.ChannelInfo);
                        }
                    });
                }
                Dispatcher.Invoke(() =>
                {
                    cb_region.Items.Clear();
                });
                for (byte i = 0; i < _devInfo.RegionCount; i++)
                {
                    var rn = await _dcs.ReadRegionName(i);
                    if (!_regionNameDic.ContainsKey(rn.Id))
                    {
                        _regionNameDic.Add(rn.Id, rn.Name);
                    }
                    else
                    {
                        _regionNameDic[rn.Id] = rn.Name;
                    }
                    if (rn.Name.Length > 0)
                    {
                        Dispatcher.Invoke(() =>
                        {
                            ComboBoxItem cbi = new ComboBoxItem
                            {
                                Content = rn.Name,
                                DataContext = rn.Id
                            };
                            if (_devStatus.Region == rn.Id)
                            {
                                cbi.IsSelected = true;
                            }
                            cbi.Selected += Cbi_Selected;
                            cb_region.Items.Add(cbi);
                        });
                    }
                }
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private async void Cbi_Selected(object sender, RoutedEventArgs e)
        {
            if (sender is ComboBoxItem cbi)
            {
                if (cbi.DataContext is byte id)
                {
                    if (_devInfo != null)
                    {
                        await _dcs.SetRegion(id);
                        for (byte i = 0; i < _devInfo.ChannelCount; i++)
                        {
                            var ch = await _dcs.ReadRfCh(i);
                            await Dispatcher.BeginInvoke(() =>
                            {
                                if (!_channelInRegionDic.ContainsKey(ch.Id))
                                {
                                    _channelInRegionDic.Add(ch.Id, ch.ChannelInfo);
                                }
                                else
                                {
                                    _channelInRegionDic[ch.Id] = ch.ChannelInfo;
                                }
                                if (!_channelButtonDic.ContainsKey(ch.Id))
                                {
                                    int row = ch.Id / 4;
                                    int col = ch.Id % 4;
                                    while (wp_ch.RowDefinitions.Count < row + 1)
                                    {
                                        wp_ch.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(50, GridUnitType.Pixel) });
                                    }
                                    MyChannelButtonBindingData data = new MyChannelButtonBindingData(ch.Id, ch.ChannelInfo);
                                    Button b = new Button();
                                    b.DataContext = data;
                                    b.SetBinding(ContentProperty, nameof(data.ButtonDisplayContent));
                                    b.SetValue(Grid.ColumnProperty, col);
                                    b.SetValue(Grid.RowProperty, row);
                                    b.SetValue(Block.TextAlignmentProperty, TextAlignment.Center);
                                    b.Click += Button_Channel_Click;
                                    wp_ch.Children.Add(b);
                                    _channelButtonDic.Add(ch.Id, b);
                                }
                                if (_channelButtonDic[ch.Id].DataContext is MyChannelButtonBindingData dd)
                                {
                                    dd.ReplaceChannelInfo(ch.ChannelInfo);
                                }
                                else
                                {
                                    _channelButtonDic[ch.Id].DataContext = null;
                                    _channelButtonDic[ch.Id].DataContext = new MyChannelButtonBindingData(ch.Id, ch.ChannelInfo);
                                }
                            });
                        }
                        UpdateMyCurrentChannelsWhenSettingChanged();
                    }
                    _logger.LogDebug($"item {cbi.Content} selected,id is {id}");
                }
            }
        }

        private async void Button_Channel_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button b)
            {
                if (b.DataContext is MyChannelButtonBindingData dd)
                {
                    if (_devSettings != null)
                    {
                        if (ic_curr_ch.SelectedItem == _currChA)
                        {
                            _devSettings.ChannelA = dd.ButtonChannelId;
                        }
                        else if (ic_curr_ch.SelectedItem == _currChB)
                        {
                            _devSettings.ChannelB = dd.ButtonChannelId;
                        }
                        try
                        {
                            await _dcs.WriteSettings(_devSettings);
                            UpdateMyCurrentChannelsWhenSettingChanged();
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    }
                }
            }
        }
        private Task<IReadOnlyCollection<BtDevInfo>?> scanBT()
        {
            if (_ccs.ClientMode == ClientModeEnum.Remote)
            {
                return _mpcss.DiscoverMyDevicesTop5();
            }
            return _dcls.DiscoverMyDevicesAsync();
        }
        private void Button_Click_14(object sender, RoutedEventArgs e)
        {
            DevMgmtWindow dmw = new DevMgmtWindow(_ccs.SavedDevMacs, scanBT);
            if (dmw.ShowDialog() == true)
            {
                _ccs.SavedDevMacs.Clear();
                _ccs.SavedDevMacs.AddRange(dmw.MacList);
                _ccs.SaveDevMacs();
                FillDevMacsComboBox();
            }
        }
        public class MyChannelButtonBindingData : INotifyPropertyChanged
        {
            private ChannelInfo _channelInfo;
            private byte _channelId;
            public string ButtonDisplayContent
            {
                get
                {
                    return _channelInfo.RxFreq == 0 ? "空" : $"{_channelInfo.Name}\r\n{_channelInfo.RxFreq / 1000000M:0.0000}";
                }
            }
            public string ButtonDisplayChNo
            {
                get
                {
                    return $"{_channelId + 1}";
                }
            }
            public byte ButtonChannelId
            {
                get
                {
                    return _channelId;
                }
            }
            public void ReplaceChannelInfo(ChannelInfo newCI)
            {
                _channelInfo = newCI;
                OnPropertyChanged(nameof(ButtonDisplayContent));
                OnPropertyChanged(nameof(ButtonDisplayChNo));
                OnPropertyChanged(nameof(ButtonChannelId));
            }
            public MyChannelButtonBindingData(byte id, ChannelInfo ci)
            {
                _channelId = id;
                _channelInfo = ci;
            }
            public event PropertyChangedEventHandler? PropertyChanged;

            private void OnPropertyChanged(string info)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
            }
        }
        public class MyCurrentChannel : INotifyPropertyChanged
        {
            private byte _id;
            private string _channelName = string.Empty;
            private int _rxFreq;
            private int _txFreq;
            private Visibility _visInRx;
            private Visibility _visInTx;
            private Visibility _visCh;
            private bool _isEmpty;
            public bool IsEmpty
            {
                get
                {
                    return _isEmpty;
                }
                set
                {
                    _isEmpty = value;
                    OnPropertyChanged(nameof(IsEmpty));
                    OnPropertyChanged(nameof(ChannelName));
                    OnPropertyChanged(nameof(RxFreqStr));
                    OnPropertyChanged(nameof(RxFreq));
                    OnPropertyChanged(nameof(TxFreqStr));
                    OnPropertyChanged(nameof(TxFreq));
                    OnPropertyChanged(nameof(VisInRx));
                    OnPropertyChanged(nameof(VisInTx));
                    OnPropertyChanged(nameof(Id));
                }
            }
            public byte Id
            {
                get
                {
                    return _isEmpty ? (byte)255 : _id;
                }
                set
                {
                    _id = value;
                    OnPropertyChanged(nameof(Id));
                }
            }
            public string ChannelName
            {
                get { return _isEmpty ? "空" : _channelName; }
                set
                {
                    _channelName = value;
                    OnPropertyChanged(nameof(ChannelName));
                }
            }
            public string RxFreqStr
            {
                get
                {
                    return _isEmpty ? "--" : $"Rx:{_rxFreq / 1000000m:0.0000}";
                }
            }
            public int RxFreq
            {
                get
                {
                    return _isEmpty ? -1 : _rxFreq;
                }
                set
                {
                    _rxFreq = value;
                    OnPropertyChanged(nameof(RxFreq));
                    OnPropertyChanged(nameof(RxFreqStr));
                }
            }
            public string TxFreqStr
            {
                get
                {
                    return _isEmpty ? "--" : $"Tx:{_txFreq / 1000000m:0.0000}";
                }
            }
            public int TxFreq
            {
                get
                {
                    return _isEmpty ? -1 : _txFreq;
                }
                set
                {
                    _txFreq = value;
                    OnPropertyChanged(nameof(TxFreq));
                    OnPropertyChanged(nameof(TxFreqStr));
                }
            }
            public Visibility VisInRx
            {
                get
                {
                    return _isEmpty ? Visibility.Hidden : _visInRx;
                }
                set
                {
                    _visInRx = value;
                    OnPropertyChanged(nameof(VisInRx));
                }
            }
            public Visibility VisInTx
            {
                get
                {
                    return _isEmpty ? Visibility.Hidden : _visInTx;
                }
                set
                {
                    _visInTx = value;
                    OnPropertyChanged(nameof(VisInTx));
                }
            }
            public Visibility VisCh
            {
                get
                {
                    return _visCh;
                }
                set
                {
                    _visCh = value;
                    OnPropertyChanged(nameof(VisCh));
                }
            }

            public event PropertyChangedEventHandler? PropertyChanged;

            private void OnPropertyChanged(string info)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
            }
        }
        public class MyBatteryInfo : INotifyPropertyChanged
        {
            private ushort _battVolt;
            public ushort BatteryVoltage
            {
                get
                {
                    return _battVolt;
                }
                set
                {
                    _battVolt = value;
                    OnPropertyChanged(nameof(BatteryVoltage));
                    OnPropertyChanged(nameof(BattVoltStr));
                    OnPropertyChanged(nameof(BattPercentStr));
                }
            }
            public string BattVoltStr
            {
                get
                {
                    return $"{_battVolt / 1000m:0.0}V";
                }
            }
            public string BattPercentStr
            {
                get
                {
                    return $"{_battVolt / 142}%";
                }
            }

            public event PropertyChangedEventHandler? PropertyChanged;

            private void OnPropertyChanged(string info)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
            }
        }

        private async void rb_single_Checked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.DoubleChannel = DoubleChannelTypeEnum.Off;
                _devSettings.Scan = false;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("single watch mode selected");
        }

        private async void rb_dual_Checked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.DoubleChannel = DoubleChannelTypeEnum.A;
                _devSettings.Scan = false;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("dual watch mode selected");
        }

        private async void cb_dualch_Checked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.DoubleChannel = DoubleChannelTypeEnum.A;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("checked event occurrded");
        }

        private async void cb_dualch_Unchecked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.DoubleChannel = DoubleChannelTypeEnum.Off;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("unchecked event occurrded");
        }

        private async void rb_scan_Checked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.Scan = true;
                _devSettings.DoubleChannel = DoubleChannelTypeEnum.Off;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("scan mode selected");
        }

        private async void rb_scan_Unchecked(object sender, RoutedEventArgs e)
        {
            if (_devSettings != null)
            {
                _devSettings.Scan = false;
                await _dcs.WriteSettings(_devSettings);
                UpdateMyCurrentChannelsWhenSettingChanged();
            }
            _logger.LogDebug("scan mode unselected");
        }

        private async void ic_curr_ch_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_chSwitching)
            {
                _chSwitching = false;
                return;
            }
            if (_devSettings != null)
            {
                if (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off || _devSettings.Scan)
                {
                    if (_devSettings.DoubleChannel != DoubleChannelTypeEnum.Off)
                    {
                        if (ic_curr_ch.SelectedItem == _currChA)
                        {
                            _devSettings.DoubleChannel = DoubleChannelTypeEnum.A;
                        }
                        else if (ic_curr_ch.SelectedItem == _currChB)
                        {
                            _devSettings.DoubleChannel = DoubleChannelTypeEnum.B;
                        }
                    }
                    else if (_devSettings.Scan)
                    {
                        if (ic_curr_ch.SelectedItem == _currChB)
                        {
                            if (_devSettings.ChannelA != _currChB.Id)
                            {
                                _devSettings.DoubleChannel = DoubleChannelTypeEnum.Off;
                                _devSettings.ChannelA = _currChB.Id;

                                _chSwitching = true;
                                MyCurrentChannel tmpCh = new MyCurrentChannel
                                {
                                    ChannelName = _currChA.ChannelName,
                                    VisCh = _currChA.VisCh,
                                    RxFreq = _currChA.RxFreq,
                                    VisInRx = _currChA.VisInRx,
                                    VisInTx = _currChA.VisInTx,
                                    Id = _currChA.Id,
                                    IsEmpty = _currChA.IsEmpty,
                                    TxFreq = _currChA.TxFreq
                                };
                                _currChA.ChannelName = _currChB.ChannelName;
                                _currChA.VisCh = _currChB.VisCh;
                                _currChA.RxFreq = _currChB.RxFreq;
                                _currChA.VisInRx = _currChB.VisInRx;
                                _currChA.VisInTx = _currChB.VisInTx;
                                _currChA.Id = _currChB.Id;
                                _currChA.IsEmpty = _currChB.IsEmpty;
                                _currChA.TxFreq = _currChB.TxFreq;

                                _currChB.ChannelName = tmpCh.ChannelName;
                                _currChB.VisCh = tmpCh.VisCh;
                                _currChB.RxFreq = tmpCh.RxFreq;
                                _currChB.VisInRx = tmpCh.VisInRx;
                                _currChB.VisInTx = tmpCh.VisInTx;
                                _currChB.Id = tmpCh.Id;
                                _currChB.IsEmpty = tmpCh.IsEmpty;
                                _currChB.TxFreq = tmpCh.TxFreq;

                            }
                            if (ic_curr_ch.SelectedItem == _currChA)
                            {
                                ic_curr_ch.SelectedItem = _currChB;
                            }
                            else if (ic_curr_ch.SelectedItem == _currChB)
                            {
                                ic_curr_ch.SelectedItem = _currChA;
                            }
                        }
                    }
                    await _dcs.WriteSettings(_devSettings);
                    UpdateMyCurrentChannelsWhenSettingChanged();
                }
            }
        }
        public async void rb_curr_ch_power_Click(object sender, RoutedEventArgs e)
        {
            if (ic_curr_ch.SelectedItem is MyCurrentChannel mcc)
            {
                if (_channelInRegionDic.TryGetValue(mcc.Id, out ChannelInfo? ci))
                {
                    if (sender == rb_curr_ch_power_high && rb_curr_ch_power_high.IsChecked == true)
                    {
                        _logger.LogDebug("high power is checked");
                        ci.TxPower = 0;
                    }
                    else if (sender == rb_curr_ch_power_middle && rb_curr_ch_power_middle.IsChecked == true)
                    {
                        _logger.LogDebug("middle power is checked");
                        ci.TxPower = -2;
                    }
                    else if (sender == rb_curr_ch_power_low && rb_curr_ch_power_low.IsChecked == true)
                    {
                        _logger.LogDebug("low power is checked");
                        ci.TxPower = -1;
                    }
                    byte result = await _dcs.WriteRfCh(new ChannelPayload(mcc.Id, ci));
                    _logger.LogDebug($"write rf ch {mcc.Id} result is {result}");
                }
            }
        }

        private void b_ptt_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                e.Handled = true;
                BeginTalk();
            }
        }

        private void b_ptt_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Released)
            {
                e.Handled = true;
                EndTalk();
            }
        }
        private void BeginTalk()
        {
            _logger.LogDebug("begin talk...");
            _mrs.StartRecording();
        }
        private void EndTalk()
        {
            _logger.LogDebug("end talk...");
            _mrs.StopRecording();
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Oem3 && e.IsDown && !e.IsRepeat)
            {
                if (!_pttStatus)
                {
                    _pttStatus = true;
                    BeginTalk();
                }
            }
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Oem3 && e.IsUp && !e.IsRepeat)
            {
                if (_pttStatus)
                {
                    _pttStatus = false;
                    EndTalk();
                }
            }
        }
        private class SystemSleepManagement
        {
            //定义API函数
            [DllImport("kernel32.dll")]
            static extern uint SetThreadExecutionState(ExecutionFlag flags);

            [Flags]
            enum ExecutionFlag : uint
            {
                System = 0x00000001,
                Display = 0x00000002,
                Continus = 0x80000000,
            }

            /// <summary>
            ///阻止系统休眠，直到线程结束恢复休眠
            /// </summary>
            /// <param name="includeDisplay">是否阻止关闭显示器</param>
            public static void PreventSleep(bool includeDisplay = false)
            {
                if (includeDisplay)
                    SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Display | ExecutionFlag.Continus);
                else
                    SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Continus);
            }

            /// <summary>
            ///恢复系统休眠
            /// </summary>
            public static void ResotreSleep()
            {
                SetThreadExecutionState(ExecutionFlag.Continus);
            }

            /// <summary>
            ///重置系统休眠计时器
            /// </summary>
            /// <param name="includeDisplay">是否阻止关闭显示器</param>
            public static void ResetSleepTimer(bool includeDisplay = false)
            {
                if (includeDisplay)
                    SetThreadExecutionState(ExecutionFlag.System | ExecutionFlag.Display);
                else
                    SetThreadExecutionState(ExecutionFlag.System);
            }
        }
    }
}